import sqlite3
from common import *
from config import *
from itertools import repeat
from twisted.internet import reactor, threads
from twisted.enterprise import adbapi       
from twisted.python.util import println  
class Tracker:   
    schema_file = 'tracker/tracker.sql'
    db = 'tracker/tracker.sqlite'   
    crypto_db = 'tracker/crypto.sqlite'
    crypto_schema_file = 'tracker/crypto.sql'         
    peers = {}
    tracker_queue = []   
    sql_results = None
    def __init__(self,
                 rpc_host=TRACKER_RPC_HOST,                  
                 rpc_port=TRACKER_RPC_PORT,
                 external_ip=TRACKER_EXTERNAL_IP,
                 external_port=TRACKER_EXTERNAL_PORT):        
        self.rpc_host = rpc_host
        self.rpc_port = rpc_port
        self.external_ip = external_ip
        self.external_port = external_port
        check_schema(self.db, self.schema_file)
        check_schema(self.crypto_db, self.crypto_schema_file)
        crypto.generate_keys(self.crypto_db)
        self.dbpool = adbapi.ConnectionPool('sqlite3', self.db, check_same_thread=False, timeout=30)                        
        self.rpc_server = RPCServer(self)
        self.public_key = crypto.get_public_key(self.crypto_db) 
    
    ##############RPC METHODS#########################333333
    def get_public_key(self):
        print "get_public_key"        
        return self.public_key
    def get_peer_address(self, peer_id):
        if peer_id not in self.peers:
            return None
        return self.peers[peer_id]
    def add_peer(self, peer_id, peer_name, host, port, pickled_pubkey):
        print "add_peer"
        self.peers[peer_id] = (peer_id, peer_name, host, port, pickled_pubkey)        
    def get_sharing_peers(self, block_ids):
        def nit(txn):
            print "get_sharing_peers"
            ret = {}            
            for bid in block_ids:
                res = txn.execute('select peer_id from blocks_peers where block_id="%s"' % bid)
                if res.rowcount == 0:
                    print "!!!!there is no peer for block_id = ", bid
                    return
                ret[bid] = []
                disconnected = []
                for row in res:
                    if row[0] in self.peers:
                        ret[bid].append(self.peers[row[0]])
                    else:
                        disconnected.append(row[0], None, None, None)
                if not ret[bid]:
                    ret[bid] = disconnected
            return ret
        d = self.dbpool.runWithConnection(nit)
        d.addErrback(errorHandler("Can not get the sharing peers from the DB"))
        return d
    def get_unused_blocks(self, num_blocks):
        print "get_unused_blocks"
        def nit(txn):
            print "get_unused_blocks.nit"
            ret = {}
            res = txn.execute('select block_id, count(peer_id)\
                as cnt from blocks_peers group by block_id order by cnt limit %d' % num_blocks)            
            for row in res:
                peers = []
                ret[row[0]] = [row[1], peers]
                res2 = txn.execute('select peer_id from blocks_peers where block_id="%s"' % row[0])
                if res2.rowcount == 0:
                    print "no peer has the block with id",row[0]                    
                for row2 in res2:
                    if row2[0] in self.peers:
                        peers.append(self.peers[row2[0]])
                    else:
                        print "the peer with id ", row2[0], " is not online"
            return ret
        d = self.dbpool.runWithConnection(nit) 
        d.addErrback(errorHandler("problem in database query"))
        return d 
    #these blocks are just shared by the user, update internal dictionaries accordingly.
    def add_share_info(self, peer_id, bids):
        print "add_share_info"   
        def nit(txn): 
            print "adding %d blocks for peer %s" % (len(bids), peer_id)           
            txn.executemany('insert or ignore into blocks_peers(peer_id,block_id) values (?,?)', zip(repeat(peer_id), bids))
        d = self.dbpool.runInteraction(nit)
        d.addErrback(errorHandler("Can not add share info to DB"))                
        return d
    
    
    def remove_peer_blocks(self, peer_id):
        def nit(txn):
            res = txn.execute('delete from blocks_peers where peer_id="%s"' % peer_id)
        d = self.dbpool.runInteraction(nit)
        d.addErrback(errorHandler("can not clear peer blocks from DB"))
        return d
    def remove_peer(self, peer_id):
        print "remove_peer"
        del self.peers[peer_id]
################### WEB UI METHODS
    def run_sql(self, query):
        def nit(txn):
            print "running sql query",query
            res = txn.execute(query)
            sql_results = []
            for row in res:
                arr = []
                for data in row:
                    if isinstance(data, buffer):
                        data = ''.join(map(lambda x:'%02x'%ord(x), data[:16]))
                    else:
                        data = str(data)
                    arr.append(data)
                sql_results.append(arr)                                                    
            print "setting sql_results"
            self.sql_results = sql_results                
        self.dbpool.runWithConnection(nit).addErrback(errorHandler("run_sql failed"))
    def reset(self):                        
        print "clearing the databases"        
        drop_tables(self.db)
        create_tables(self.db, self.schema_file)
def errorHandler(msg):
    def nit(failure):
        print str(failure)
        raise Exception(msg)
    return nit
